package com.huilan.base.esl.service.acd.domain.model.agent;

import cn.hutool.core.util.StrUtil;
import com.huilan.base.esl.service.acd.domain.model.ACDException;
import com.huilan.base.esl.service.acd.domain.model.agent.event.AgentOnlineEvent;
import com.huilan.base.esl.service.acd.domain.model.phoneinline.PhoneInline;
import com.huilan.base.esl.service.acd.domain.model.queue.Queue;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLiveObjectService;
import org.redisson.api.RLock;
import org.redisson.api.annotation.REntity;
import org.redisson.api.annotation.RId;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.huilan.base.esl.service.acd.domain.model.Base.getEslClient;
import static com.huilan.base.esl.service.acd.domain.model.Base.getRedisson;
import static com.huilan.base.esl.service.acd.domain.model.Base.publish;

@Slf4j
@Getter
@Setter
@REntity
public class Agent{

    // 分机号
    @RId
    private String id;
    private String ext;
    // 状态
    // 0-默认  1-可用 2-呼叫中 3-处理排队中的任务(准备上线)
    // 11-休息 12-坐席未登录
    private int status;
    private Set<Queue> queues;

    private static String lockKey = "temp:agent:lock:";
    public Agent() {
    }

    public Agent(String id, String ext) {
        this.id = id;
        this.ext = ext;
    }

    public static Agent create(String id,String ext){
        Agent agent = get(id);
        if (agent != null){
            return agent;
        }
        RLiveObjectService liveObjectService = getRedisson().getLiveObjectService();
        agent = liveObjectService.persist(new Agent(id,ext));
        agent.setQueues(new HashSet<>());
        return agent;
    }
    public static Agent get(String id){
        RLiveObjectService liveObjectService = getRedisson().getLiveObjectService();
        return liveObjectService.get(Agent.class, id);
    }
    public void agentIntoQueue(String agentId,String queueId){
        Queue queue = Queue.get(queueId);
        addQueue(queue);

        Agent agent = get(agentId);
        queue.addAgent(agent);
    }

    private void setStatusWithLock(int status){
        RLock lock = getRedisson().getLock(lockKey + getId());
        try {
            lock.lock(1, TimeUnit.SECONDS);
            setStatus(status);
        }catch (Exception e){
        }finally {
            lock.unlock();
        }
    }
    // 坐席休息
    public void rest(){
        setStatusWithLock(11);
    }
    // 坐席掉线了
    public void notRegistered(){
        setStatusWithLock(12);
    }
    // 开始上班
    public boolean startWork(){
        boolean b = false;
        RLock lock = getRedisson().getLock(lockKey + getId());
        try {
            b = lock.tryLock(1, TimeUnit.SECONDS);
            if (b)
                setStatus(2);
        }catch (Exception e){
        }finally {
            try {
                lock.unlock();
            }catch (Exception e){ }
        }
        return b;
    }
    // 坐席上线
    public void online(){
        online(false);
    }
    // checkStatus 检查当前坐席状态
    // 坐席直接上线不用检查
    // 挂机恢复/异常恢复的时候 需要检查状态
    public void online(boolean checkStatus){
        try {
            if (checkStatus == false){
                setStatusWithLock(3);
            }
            boolean b = handleQueuedUsers(checkStatus);
            // 没有用户需要处理
            if (b == false) {
                log.info("agent:{} 坐席上线", getExt());
                setStatusWithLock(1);
                publish(new AgentOnlineEvent(this, this));
            }
        }catch (ACDException a) {
            log.warn("ACDException:{}",a.getMsg());
        }
    }
    // 处理排队用户
    private boolean handleQueuedUsers(boolean checkStatus){
        String ext = getExt();
        log.info("agent:{} 开始处理排队中的用户",ext);
        int status = getStatus();
        if (checkStatus && status > 10){
            throw new ACDException("坐席已经下线了");
        }
        // 坐席关联的队列
        Set<Queue> queues = getQueues();
        // 排队中的用户
        List<PhoneInline> queuedUsers = queues.stream().flatMap(s -> s.getPhoneInlines().stream()).collect(Collectors.toList());
        if (queuedUsers == null || queuedUsers.size() == 0){
            log.info("agent:{} 没有人排队",ext);
            return false;
        }else{
            // 有人排队
            PhoneInline phoneInline = PhoneInline.selectAQueuedUser(queuedUsers);
            if (phoneInline == null){
                log.info("agent:{} 有人排队 但是有锁 获取不到",ext);
                return false;
            }else{
                boolean calling = phoneInline.getChannel().calling();
                if (calling == false){
                    log.info("agent:{} 找到一个排队用户 准备处理的时候发现用户已经挂掉了 递归接着找 phoneInline:{}",ext,phoneInline.getPhone());
                    for (Queue queue:queues) {
                        queue.delPhoneInline(phoneInline);
                    }
                    phoneInline.setAgent(null);
                    PhoneInline.getBlock(phoneInline.getPhone()).countDown();
                    return handleQueuedUsers(checkStatus);
                }
                log.info("agent:{} 响应排队用户 :{}",ext,phoneInline.getPhone());
                phoneInline.setAgent(this);
                PhoneInline.getBlock(phoneInline.getPhone()).countDown();
                return true;
            }
        }
    }

    // 是空闲的  无锁坐席
    public boolean idle(){
        RLock lock = getRedisson().getLock(lockKey + getId());
        return getStatus() == 1 && lock.isLocked() == false;
    }

    public boolean transfer(PhoneInline phoneInline) {
        String phone = phoneInline.getPhone();
        String ext = getExt();
        boolean calling = phoneInline.getChannel().calling();
        if (calling == false){
            log.warn("phone:{} 用户已挂机",phone);
            throw new ACDException("用户已挂机");
        }
        String command = StrUtil.format("originate user/{} &intercept({})",ext,phoneInline.getChannel().getUuid());
        List<String> stringList = null;
        try {
            log.info("phone:{} 坐席呼叫中 agent:{}",phone,ext);
            stringList = getEslClient().getClient().sendAsyncApiCommand(command, "", false,120);
        }catch (Exception e){
            log.warn("呼叫失败 :{}",e);
        }
        if (stringList != null && StrUtil.contains(stringList.get(0),"OK") ){
            return true;
        }else{
            checkfail(stringList,phone,ext);
            return false;
        }
    }
    // 检查失败原因
    public void checkfail(List<String> stringList,String phone,String ext){

        //  检查失败原因
        TransferFailedEnum reason = TransferFailedEnum.getReason(stringList);
        log.info("phone:{} ext:{} 检查失败原因  reason:{} stringList:{}",phone,ext,reason.getDescription(),stringList);

        switch (reason){
            case USER_NOT_REGISTERED:
                notRegistered();
                break;
            case CALL_REJECTED:
                break;
        }

    }

    public void addQueue(Queue queue){
        getQueues().add(queue);
    }
    public void delQueue(Queue queue){
        getQueues().remove(queue);
    }

}
